Rest 01: L’approche RESTFul

Université de Toulon

LIS UMR CNRS 7020

2024-10-03

Source
Branch
  • develop (43d1d31)
  • 2024-03-08 10:28:50
Java
  • OpenJDK Temurin-21.0.4+7
  • Apache Maven 3.9.9
Docker
  • Client: 27.2.0 - buildx v0.16.2 - compose v2.29.7
  • Server: 27.2.0

Objectifs

Ce document présente les service Web REST en général et par la pratique en Java.

Il s’appuie sur un exemple simple d’application : https://github.com/ebpro/sample-jaxrs qui servira à illustrer les notions et sera étudiée en détail dans la partie pratique.

L’Approche REST (REpresentational State Transfer)

  • Qu’est-ce que REST ?
    • REpresentational State Transfer
    • Un style architectural pour la conception de services web
    • Offre un accès distant à des ressources via HTTP
  • Principes de REST
    • Interface uniforme en HTTP
    • Stateless : Chaque requête est indépendante
    • Cacheable : Possibilité de mettre en cache les réponses
    • Client-Server : Séparation claire des responsabilités

RESTful

  • Une approche conformant à ces principes
  • Flexibilité dans la conception des API
  • Pas une norme, mais une recommandation d’architecture

Important

RESTfull est une approche d’API client/serveur suivant la logique de navigation dans un hypermedia. On parle d’HATEOS (Hypermedia As The Engine Of Application State).

Protocole de communication

Pour définir un protocole de communication, il faut généralement définir :

  • un système d’identification (d’adressage) des ressources manipulées,
  • un protocole de communication,
  • un format d’échange de données éventuellement typées,
  • un système de gestion des erreurs.

La logique RESTfull est d’utiliser tout ce que propose HTTP pour écrire une API en HTTP.

L’adressage des ressources

Important

Les ressources (ou ensembles de ressources) de l’application sont identifiées par des URI. Les URL sont une sorte particulière d’URI qui indique un moyen d’accès en plus de les identifier de façon unique.

Il n’y a pas de standard pour les API REST. Il vaut généralement mieux rester simple et cohérent. Quelques pratiques sont utilisées classiquement :

  • On utilise des noms (pas des verbes) au pluriel pour les ressources :

    # Toutes les personnes
    http://MyServer/MyApp/Persons
  • Utilisation du chemin pour inclure “paramètres”

    # La personne d'identifiant 1
    http://MyServer/MyApp/Persons/1

L’adressage des ressources (“Jointures”)

  • Eviter les “jointures”.
    • Les chiens de la personne 1
      • http://MyServer/MyApp/Persons/1/Dogs
      • http://MyServer/MyApp/Dogs?master_id=1

L’adressage des ressources (Pagination et tri)

  • Pagination
    • La deuxième page de personnes en utilisant des pages de 10 personnes.
      • http://MyServer/MyApp/Persons?page=2&page_size=10
      • http://MyServer/MyApp/Persons;page=2;page_size=10 (avec des Matrix Params)
    • filtre qui trie par ordre decroissant de date de création, puis par titre
      • http://MyServer/MyApp/Persons?page=&page_size=10&sort=name,firstname,-created,title
  • Projection
    • La personne d’identifiant 1 restreinte uniquement à certains champs
      • http://MyServer/MyApp/Persons/1?fields=email,firstname,lastname
  • Version : préfixes /api/v1, /api/v2, …

Le protocole d’échange

Verbe HTTP Utilisation Contraintes
GET Accès à une ressource identifiée dans l’URL (il peut s’agir d’une collection). Safe, Idempotent
HEAD comme GET mais sans le corps de la requête (seul le header http est retourné). Utile pour savoir si une ressource a changé. Safe, Idempotent
POST création d’une ressource sans donner l’identifiant.
PUT mise à jour complète d’une ressource identifiée (voire création en donnant l’identifiant). Idempotent
DELETE suppression d’une ressource. Idempotent
OPTIONS liste les actions possibles sur une ressource. Safe, Idempotent
PATCH RFC 5789, mises à jour partielle d’une ressource.

Endpoints

Un endpoint REST est défini par un verbe HTTP et une URL.

  • Obtenir toutes les personnes :
    • GET http://MyServer/MyApp/Persons
  • Obtenir une personne précise par identifiant :
    • GET http://MyServer/MyApp/Persons/1
  • Obtenir toutes les personnes entre 7 et 16ans (avec un filtre) :
    • GET http://MyServer/MyApp/Persons?ageMin=7&ageMax=16
  • Supprimer toutes personnes
    • DELETE http://MyServer/MyApp/Persons
  • Supprimer une personne
    • DELETE http://MyServer/MyApp/Persons/1

La représentation des ressources

Important

Les resources sont généralement représentées et échangées à l’aide de langages autodescriptifs comme XML ou JSON.

Par exemple, une personne peut être présentée :

<?xml version='1.0'?>
<person id='1'>
    <lastname>Doe</lastname>
    <firstname>John</firstname>
</person>
{
  "person": {
    "-id": 1,
    "lastname": "Doe",
    "firstname": "John"
  }
}
  • Les types de données envoyées ou attendues sont spécifiés dans l’entête de la requête HTTP par les champs Content-Type: et Accept:
  • Cette spécification utilise les Internet Media Types (ex. MIME Type - Multipurpose Internet Mail Extensions)
  • Il s’agit d’une liste standard de formats et sous-formats d’échange de données, incluant des exemples tels que text/plain, text/xml, application/json, …

Exemple de graphe

L’exemple ci-dessous sérialise des objets Java qui représente un auteur et un livre en JSON et en XML. (Le détail est expliqué plus loin).

{
  "books" : [ {
    "id" : 1,
    "title" : "Effective Java (English Edition)",
    "authors" : [ 1 ]
  } ],
  "authors" : [ {
    "id" : 1,
    "name" : "Bloch",
    "firstname" : "Joshua",
    "books" : [ 1 ]
  } ]
}

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ebjax:library xmlns:ebjax="http://bruno.univ-tln.fr/sample-jaxrs" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <authors>
        <author id="Author-1">
            <id>1</id>
            <name>Bloch</name>
            <firstname>Joshua</firstname>
            <books>
                <book>Book-1</book>
            </books>
        </author>
    </authors>
    <books>
        <book id="Book-1">
            <id>1</id>
            <title>Effective Java (English Edition)</title>
            <authors>
                <author>Author-1</author>
            </authors>
        </book>
    </books>
</ebjax:library>

Les code de retours

Important

Le code de retour des méthode est un code HTTP. Il est indiqué de façon standard dans l’entête de la réponse et peut être répété dans le contenu si une enveloppe est proposée.

Succès

Code Signification Usage
200 Ok Requête traitée avec succès.
201 Created Nouvelle ressource créée.
204 No Content Pas de contenu, pas exmple lors d’une requête DELETE réussie.
206 Partial Content Seulement une partie de résultat est retourné par exemple en cas de pagination (non explicite).
304 Not Modified Utilisation du cache possible.

Echec

Code Signification Usage
400 Bad Request La requête est invalide et ne peut pas être traitée par le serveur.
401 Unauthorized La requête nécessite que le client soit authentifié.
403 Forbidden Le client est authentifié mais l’utilisateur n’est pas autorisé à accéder à cette ressource.
404 Not Found La ressource demandée n’existe pas.
500 Internal Server Error C’est une erreur générique de fonctionnement, elle devrait toujours être accompagnée d’une description

Un échange REST = un échange HTTP

Important

Un échange d’une API REST correspond donc exactement à un échange http.

  • Requête HTTP
    • Verbe, URL, version HTTP, en-tête, corps (éventuellement vide).
  • Réponse HTTP
    • Code de retour, métadonnées dans l’en-tête, corps (éventuellement encapsulé).

Quelques exemples complets

Requête de création d’une personne :

POST http://MyServer:8080/MyApp/Persons/
Host: MyServer:8080
Content-Type: application/json; charset=utf-8
Content-Length: 36
{"lastname": "Doe",
 "firstname": "John"}

Requête de modification d’une personne (id dans l’URL):

PUT http://MyServer:8080/MyApp/Persons/1
Host: MyServer:8080
Content-Type: application/json; charset=utf-8
Content-Length: 12
{"age":"18"}

Utilisation

Une requête REST peut être envoyée par programmation ou en utilisant un programme dédié comme curl en ligne de commande, postman pour chrome ou RestClient pour firefox.

Regardez les options de la commande curl pour réaliser des requêtes HTTP.

La requête suivante utilise la commande curl pour soumettre une requête REST GET à l’API de GitHub pour consulter le profile du compte ebpro. Elle affiche le détails des requêtes et réponses HTTP

%%shell 
curl -s -D - https://api.github.com/users/ebpro
HTTP/2 200 
date: Thu, 03 Oct 2024 13:06:16 GMT
content-type: application/json; charset=utf-8
cache-control: public, max-age=60, s-maxage=60
vary: Accept,Accept-Encoding, Accept, X-Requested-With
etag: W/"fcf21b6b6d9f375971255869daf9179752a1890d0d0ce3caac9416c27c134b66"
last-modified: Sat, 28 Sep 2024 12:40:18 GMT
x-github-media-type: github.v3; format=json
x-github-api-version-selected: 2022-11-28
access-control-expose-headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
access-control-allow-origin: *
strict-transport-security: max-age=31536000; includeSubdomains; preload
x-frame-options: deny
x-content-type-options: nosniff
x-xss-protection: 0
referrer-policy: origin-when-cross-origin, strict-origin-when-cross-origin
content-security-policy: default-src 'none'
server: github.com
x-ratelimit-limit: 60
x-ratelimit-remaining: 59
x-ratelimit-reset: 1727964376
x-ratelimit-resource: core
x-ratelimit-used: 1
accept-ranges: bytes
content-length: 1329
x-github-request-id: B468:3A918C:1A75D3D:1AC68D3:66FE96C8

{
  "login": "ebpro",
  "id": 76050356,
  "node_id": "MDEyOk9yZ2FuaXphdGlvbjc2MDUwMzU2",
  "avatar_url": "https://avatars.githubusercontent.com/u/76050356?v=4",
  "gravatar_id": "",
  "url": "https://api.github.com/users/ebpro",
  "html_url": "https://github.com/ebpro",
  "followers_url": "https://api.github.com/users/ebpro/followers",
  "following_url": "https://api.github.com/users/ebpro/following{/other_user}",
  "gists_url": "https://api.github.com/users/ebpro/gists{/gist_id}",
  "starred_url": "https://api.github.com/users/ebpro/starred{/owner}{/repo}",
  "subscriptions_url": "https://api.github.com/users/ebpro/subscriptions",
  "organizations_url": "https://api.github.com/users/ebpro/orgs",
  "repos_url": "https://api.github.com/users/ebpro/repos",
  "events_url": "https://api.github.com/users/ebpro/events{/privacy}",
  "received_events_url": "https://api.github.com/users/ebpro/received_events",
  "type": "Organization",
  "site_admin": false,
  "name": "EBPro",
  "company": null,
  "blog": "https://bruno.univ-tln.fr",
  "location": "France",
  "email": "emmanuel.bruno@univ-tln.fr",
  "hireable": null,
  "bio": null,
  "twitter_username": null,
  "public_repos": 69,
  "public_gists": 0,
  "followers": 1,
  "following": 0,
  "created_at": "2020-12-15T12:56:09Z",
  "updated_at": "2024-09-28T12:40:18Z"
}